/* Include files. */
extern "C" { 
	#include "standard.h"
	#include "util.h"
	#include "debug.h"
}

#include "child.h"
#include "script.h"
#include "subsystemcore.h"
#include "event.h"
#include "cplusplus.h"
#include "timercore.h"

/* Macro constant definitions. */

/* Local function declarations. */

/* Macro API definitions. */

/* Global variable declarations. */

extern int parent_pipe[2];
static queue<struct script*> script_queue;

#ifdef U_LOOP
static uloop_fd parent_handle[2];
#else
extern struct ev_loop *main_loop;
static ev_io parent_handle[2];
#endif

/* Type definitions. */

/* Local function definitions. */

#ifdef U_LOOP
static void _sendScript(uloop_fd *h, 
					unsigned int revents)
#else
static void _sendScript(struct ev_loop *loop, 
					ev_io *h, int revents)
#endif
{
	while(script_queue.size()){
		struct script *s = script_queue.front();
		script_queue.pop();
		int total = sizeof(*s) + s->len;
		int ret = pipeWrite(h->fd, (const char *)s, total);
		if (ret < 0) {
			ASSERT(0);
			script_queue.push(s);
			return;
		}
		else if (ret == 0){
			script_queue.push(s);
			return;
		}
		else {
			ASSERT(ret == total);
			free(s);
		}
	}
	if (!script_queue.size()) {
		
#ifdef U_LOOP
		uloop_fd_add(h, ULOOP_READ);
#else
		ev_io_stop (loop, h);
#endif

	}
	
	return;
}

static void sendScript()
{

#ifdef U_LOOP
	uloop_fd_add(&parent_handle[0], ULOOP_READ|ULOOP_WRITE );
#else
	ev_io_stop (main_loop, &parent_handle[1]);
	ev_io_init (&parent_handle[1], _sendScript, parent_pipe[1], EV_WRITE);
	ev_io_start (main_loop, &parent_handle[1]);
#endif

}

static int recvHeader(int fd, struct script *s)
{	
	int ret = pipeRead(fd, (char *)&(*s), sizeof(*s));
	if (ret > 0){
		ASSERT(ret == sizeof(*s));
	}
	return ret;
}

#ifdef U_LOOP
static void _recvScriptResult(uloop_fd *h, 
					unsigned int revents)
#else
static void _recvScriptResult(struct ev_loop *loop, 
						ev_io *h, int revents)
#endif
{
	struct script r;
	char buff[MAX_SCRIPT_RESULT_LEN];

#ifdef U_LOOP
	if(revents & ULOOP_WRITE) {
		_sendScript(h, revents);
	}
	if(!(revents & ULOOP_READ))
		return;
#endif

	int ret = recvHeader(h->fd, &r);
	if (ret > 0) {
		ret = pipeRead(h->fd, (char *)buff, r.len);
		if (ret >= 0) {
			ASSERT(ret < MAX_SCRIPT_RESULT_LEN);
			ASSERT(ret == r.len);			
			buff[r.len] = '\0';
			event *ev = r.ev;
			subSystem *sub = ev->_sub;
			if (r.finish == e_SHELL_NOR) {
				sendSubSystem(ev->_L, sub->_name.c_str(), sub->_name.c_str(), 
					"response", buff, ev->_session);
			}
			else if (r.finish == e_SHELL_FINISH) {
				sub->_cost_cpu += ev->costTime();
				char buf[64];
				memset(buf, 0, 64);
				sprintf(buf, "EOF.%d", r.code);
				sendSubSystem(ev->_L, sub->_name.c_str(), sub->_name.c_str(), 
					"response", buf, ev->_session);
				delete ev;
				DEBUG("shellover(%05d), code = %d\n", r.pid, r.code);
			}
		}
	}
	return;
}

void recvScriptResult()
{

#ifdef U_LOOP
	parent_handle[0].cb = _recvScriptResult;
	parent_handle[0].fd = parent_pipe[1];
	uloop_fd_add(&parent_handle[0], ULOOP_READ);
#else
	ev_io_stop (main_loop, &parent_handle[0]);
	ev_io_init (&parent_handle[0], _recvScriptResult, parent_pipe[1], EV_READ);
	ev_io_start (main_loop, &parent_handle[0]);
#endif

}

static void shellAsync(const char *cmd)
{
	int len = strlen(cmd) + 1;
	struct script *s = (struct script*)malloc(sizeof(*s) + len);
	memset(s, 0, sizeof(*s) + len);
	s->len = len;
	s->type = e_ASYNC;
	strcpy(s->data, cmd);
	script_queue.push(s);
	sendScript();
	DEBUG("shell    (%s)\n", cmd);
	return;
}

static void shellSync(const char *cmd, event *ev)
{
	int len = strlen(cmd) + 1;
	struct script *s = (struct script*)malloc(sizeof(*s) + len);
	memset(s, 0, sizeof(*s) + len);
	s->ev = ev;
	s->len = len;
	s->type = e_SYNC;
	strcpy(s->data, cmd);
	script_queue.push(s);
	sendScript();
	DEBUG("shellsync(%s)(%u)\n", cmd, ev->_session);
	return;
}

static void shellKill(event *ev)
{
	int len = 0;
	struct script *s = (struct script*)malloc(sizeof(*s) + len);
	memset(s, 0, sizeof(*s) + len);
	s->ev = ev;
	s->len = len;
	s->type = e_KILL;
	script_queue.push(s);
	sendScript();
	DEBUG("shellkill(%u)\n", ev->_session);
	return;
}

static int getIfhwaddr(int fd, const char *ifname, 
				unsigned char *hwaddr)
{
	int ret = -1;
	struct ifreq ifr;
	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));

	ret = ioctl(fd, SIOCGIFHWADDR, &ifr);

	if(ret == 0)
		memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, 6);

	return ret;
}

static void doIfconfig(const char *ifname, char *out)
{
	int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
	do {
		if(fd < 0)
			break;

		int ret;
		unsigned char hwaddr[6];
		ret = getIfhwaddr(fd, ifname, hwaddr);
		if(ret == 0) {
			sprintf(out,"%s HWaddr %02X:%02X:%02X:%02X:%02X:%02X",
					ifname,
					hwaddr[0], hwaddr[1], hwaddr[2], 
					hwaddr[3], hwaddr[4], hwaddr[5]);
		}	
		close(fd);
	}while(0);
}

int luaIfconfig(lua_State *L) 
{
	char out[128];
	memset(out, 0 , 128);
	const char* str = lua_tostring(L, -1);
	doIfconfig(str, out);
	lua_pushstring(L, out);
	return 1;
}

int luaShellAsync(lua_State *L) 
{
	const char* str = lua_tostring(L, -1);
	shellAsync(str);
	return 0;
}

int luaShellSync(lua_State *L) 
{
	const char* str = lua_tostring(L, 1);
	u32 session = lua_tointeger(L, 2);
	event *ev = new event(session, L);
	shellSync(str, ev);
	lua_pushlightuserdata(L, ev);
	return 1;
}

int luaShellKill(lua_State *L) 
{
	event *ev = (event*)lua_touserdata(L, 1);
	shellKill(ev);
	return 0;
}


